package com.gr.staffpm.pages.tasks;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Set;

import org.apache.shiro.SecurityUtils;
import org.apache.wicket.PageParameters;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.basic.MultiLineLabel;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.wicketstuff.annotation.mount.MountPath;
import org.wicketstuff.annotation.strategy.MountHybrid;

import com.gr.menuw.horizontal.dropdown.multilevel.MultiLevelMenu;
import com.gr.menuw.menu.item.AjaxLinkMenuItem;
import com.gr.menuw.menu.item.dialog.DialogMenuItem;
import com.gr.staffpm.StaffPMWebPage;
import com.gr.staffpm.datatypes.Group;
import com.gr.staffpm.datatypes.Task;
import com.gr.staffpm.datatypes.TaskComment;
import com.gr.staffpm.datatypes.TaskQueue;
import com.gr.staffpm.datatypes.User;
import com.gr.staffpm.model.StringModel;
import com.gr.staffpm.notes.activity.service.IActivityDaoService;
import com.gr.staffpm.pages.error.UnauthorizedPage;
import com.gr.staffpm.pages.management.review.command.AcceptCommand;
import com.gr.staffpm.pages.management.review.command.IReviewCommandReciever;
import com.gr.staffpm.pages.management.review.command.RejectCommand;
import com.gr.staffpm.pages.management.review.command.SubmitFeedbackCommand;
import com.gr.staffpm.pages.tasks.model.AjaxCloseOpenTaskLink;
import com.gr.staffpm.pages.tasks.model.AjaxOpenProgressTaskLink;
import com.gr.staffpm.pages.tasks.model.AjaxResolveTaskLink;
import com.gr.staffpm.security.constants.StaffPMRoles;
import com.gr.staffpm.security.service.UserService;
import com.gr.staffpm.tasks.service.TaskQueueService;
import com.gr.staffpm.tasks.service.TaskService;
import com.gr.staffpm.widget.dialog.feedback.AjaxFeedbackDialog;
import com.gr.staffpm.widget.dialog.feedback.AjaxRejectDialog;
import com.gr.staffpm.widget.dialog.task.AjaxAssignWindow;

@MountHybrid
@MountPath(path = "secure/ViewTask")
public class ViewTask extends StaffPMWebPage implements IReviewCommandReciever {

    @SpringBean(name = "userService")
    private UserService userService;
    @SpringBean(name = "taskService")
    private TaskService taskService;
    @SpringBean(name = "taskQueueService")
    private TaskQueueService taskQueueService;
    @SpringBean(name = "activityService")
    private IActivityDaoService activityDaoService;
    /** A global list of all comments from all users across all sessions */
    private ListView<TaskComment> comments;
    /** Container for the comments, used to update the listview. */
    private WebMarkupContainer commentsContainer;
    private CommentForm commentForm;
    private List<TaskComment> commentList;
    private AjaxOpenProgressTaskLink startStopTaskLink;
    private AjaxResolveTaskLink resolveTaskLink;
    private AjaxCloseOpenTaskLink closeOpenTaskLink;
    private AjaxLinkMenuItem openProgressMenuItem, openClosedMenuItem, resolutionMenuItem;
    private MultiLevelMenu optionsMenu, reviewMenu;
    private WebMarkupContainer content;
    private Task task;
    private Label taskStatus, taskResolution, taskResolver, taskUpdDate, taskNotice, taskResDate, taskAssignee;
    private final Label taskResDateLabel = new Label("taskResDateLabel", "Resolution Date:");
    private final Label taskResolverLabel = new Label("taskResolverLabel", "Resolved By:");
    private BookmarkablePageLink<Void> editPage;
    private DialogMenuItem assignMenuItem;
    private AjaxFeedbackDialog feedbackDialog;
    private AjaxRejectDialog rejectDialog;
    private final SimpleDateFormat date = new SimpleDateFormat("dd MMM yyyy");
    boolean owner, admin, readable, editable, commentable;

    //private final Logger log = LoggerFactory.getLogger(getClass());

    public ViewTask(PageParameters parameters) {
        super();
        this.task = taskService.getTask(parameters.getInt("id"));
        User currUser = userService.getCurrentUser();
        owner = currUser.equals(task.getAssignee()) || currUser.equals(task.getCreatedBy());
        admin = SecurityUtils.getSubject().hasRole(StaffPMRoles.ADMIN);
        readable = SecurityUtils.getSubject().isPermitted("project:readtasks:" + task.getProject().getProjectId()) || SecurityUtils.getSubject().isPermitted("task:read:" + task.getTaskId());
        editable =
                SecurityUtils.getSubject().isPermitted("project:updatetasks:" + task.getProject().getProjectId())
                        || SecurityUtils.getSubject().isPermitted("task:update:" + task.getTaskId());
        commentable =
                SecurityUtils.getSubject().isPermitted("project:commenttasks:" + task.getProject().getProjectId())
                        || SecurityUtils.getSubject().isPermitted("task:comment:" + task.getTaskId());

        if (readable || admin || owner) {
            super.onBeforeRender();
            content = new WebMarkupContainer("content");
            content.setOutputMarkupId(true);

            //Header
            add(new Label("projectName", "Project: " + task.getProject().getProjectName()).setEscapeModelStrings(true));
            add(new Label("taskName", "Task: " + task.getName()).setEscapeModelStrings(true));
            add(new Label("taskSummary", "Summary: " + task.getSummary()).setEscapeModelStrings(true));

            //Options
            editPage = new BookmarkablePageLink<Void>("editLink", EditTask.class);
            editPage.setParameter("id", String.valueOf(task.getTaskId()));
            editPage.setVisible((!task.isQueued() && !(task.getStatus().equals(taskService.getClosedStatus()))) && (admin || owner || editable));
            content.add(editPage);
            content.add(buildOptionsMenu());
            content.add(buildReviewMenu());

            taskNotice = (Label) new Label("notice", "Task is queued for management to review.").setOutputMarkupId(true);
            taskNotice.setVisible(task.isQueued());
            content.add(taskNotice);

            //Details
            content.add(taskAssignee = new Label("taskAssignee", task.getAssignee().getFullName()));
            taskAssignee.setEscapeModelStrings(true);
            content.add(new Label("taskCreator", task.getCreatedBy().getFirstName() + " " + task.getCreatedBy().getLastName()).setEscapeModelStrings(true));
            content.add(new Label("taskImportance", task.getImportance().getValue()));
            content.add(taskResolverLabel);
            taskResolverLabel.setVisible(task.isResolved());
            taskResolverLabel.setOutputMarkupId(true);
            taskStatus = new Label("taskStatus", new StringModel(task.getStatus().getValue()));
            taskStatus.setOutputMarkupId(true);
            content.add(taskStatus);
            taskResolver = new Label("taskResolver", new StringModel(""));
            taskResolver.setOutputMarkupId(true);
            content.add(taskResolver);

            //Dates
            content.add(new Label("taskDueDate", date.format(task.getDueDate())));
            taskUpdDate = new Label("taskUpdDate", new StringModel(date.format(task.getLastUpdated())));
            taskUpdDate.setOutputMarkupId(true);
            content.add(taskUpdDate);
            content.add(new Label("taskCreatedDate", date.format(task.getCreated())));
            taskResolution = new Label("taskResolution", new StringModel(task.isResolved() ? "Resolved" : "Unresolved"));
            taskResolution.setOutputMarkupId(true);
            content.add(taskResolution);

            Date resDate = Calendar.getInstance().getTime();
            if (task.isResolved()) {
                resDate = task.getResolvedDate();
                taskResolver.setDefaultModel(new StringModel(task.getResolvedBy().getFullName()));
            }

            taskResDate = new Label("taskResDate", date.format(resDate));
            taskResDateLabel.setVisible(task.isResolved());
            taskResDate.setVisible(task.isResolved());
            taskResolver.setVisible(task.isResolved());

            content.add(taskResDateLabel);
            content.add(taskResDate);

            //Description
            content.add(new MultiLineLabel("taskDescription", task.getDescription()).setEscapeModelStrings(true));

            //Comments
            commentList = taskService.getAllComments(task);
            // the WebMarkupContainer is used to update the listview in an ajax call
            commentsContainer = new WebMarkupContainer("commentsContainer");
            commentsContainer.setOutputMarkupId(true);
            content.add(commentsContainer);

            // Add comments of existing comments
            comments = new ListView<TaskComment>("comments", new PropertyModel<List<TaskComment>>(this, "commentList")) {

                private static final long serialVersionUID = 1L;

                @Override
                protected void populateItem(ListItem<TaskComment> item) {
                    final TaskComment comment = item.getModelObject();
                    item.add(new Label("commentBy", comment.getCommentHeader()));
                    item.add(new MultiLineLabel("comment", comment.getComment()).setEscapeModelStrings(true));
                }
            };
            comments.setVersioned(false);
            comments.setOutputMarkupId(true);
            commentsContainer.add(comments);

            commentForm = new CommentForm("commentForm", taskService, task, commentList);
            commentForm.setVisible(commentable || admin || owner || editable);
            content.add(commentForm);

            commentForm.add(new CommentFormAjaxSubmitBehavior(commentForm, commentsContainer));
            add(content);

            feedbackDialog = new AjaxFeedbackDialog("feedbackDialog", new SubmitFeedbackCommand(this));
            addOrReplace(feedbackDialog);
            rejectDialog = new AjaxRejectDialog("rejectDialog", new SubmitFeedbackCommand(this));
            addOrReplace(rejectDialog);
        } else
            setResponsePage(UnauthorizedPage.class);
    }

    /**
     * @return
     */
    private MultiLevelMenu buildReviewMenu() {
        reviewMenu = new MultiLevelMenu("review-options-menu");
        reviewMenu.setOutputMarkupId(true);
        reviewMenu.setRenderBodyOnly(false);

        AjaxLinkMenuItem acceptMenuItem = new AjaxLinkMenuItem("1", "Accept", new AjaxReviewLink(new AcceptCommand(this)));
        reviewMenu.addItem(acceptMenuItem);

        AjaxLinkMenuItem rejectMenuItem = new AjaxLinkMenuItem("2", "Reject", new AjaxReviewLink(new RejectCommand(this)));
        reviewMenu.addItem(rejectMenuItem);

        //TODO Check if manager has permission to review
        if (!SecurityUtils.getSubject().hasRole(StaffPMRoles.MANAGER) || !task.isQueued()) {
            reviewMenu.setVisible(false);
        }

        return reviewMenu;
    }

    private MultiLevelMenu buildOptionsMenu() {
        optionsMenu = new MultiLevelMenu("options-menu");
        optionsMenu.setOutputMarkupId(true);
        optionsMenu.setRenderBodyOnly(false);

        User currUser = userService.getCurrentUser();
        Set<Group> groups = currUser.getGroups();
        final List<User> team = new ArrayList<User>();
        team.addAll(userService.getUsersInGroupByGroupName(groups.iterator().next().getName(), currUser, userService.getUsersHighestLevel(currUser)));
        final AjaxAssignWindow assignWndw = new AjaxAssignWindow("assignDialog", team, task) {

            private static final long serialVersionUID = 1L;

            @Override
            protected void submit(AjaxRequestTarget target, String comment, User user) {
                User currUser = userService.getCurrentUser();
                if (!comment.isEmpty()) {
                    TaskComment taskComment = new TaskComment();
                    taskComment.setComment(comment);
                    taskComment.setCreated(new Date());
                    taskComment.setCreatedBy(currUser);
                    taskComment.setLastUpdated(new Date());
                    taskComment.setUpdatedBy(currUser);
                    taskComment.setTask(task);
                    taskService.addComment(taskComment);
                }

                task.setAssignee(user);
                taskService.updateTask(task);
                taskAssignee.setDefaultModelObject(task.getAssignee().getFullName());
                target.addComponent(content);
                this.close(target);
            }
        };
        add(assignWndw);

        assignMenuItem = new DialogMenuItem("1", "Assign", assignWndw);
        assignMenuItem.enabled((!task.isQueued()) && (admin || owner || editable));
        optionsMenu.addItem(assignMenuItem);

        startStopTaskLink = new AjaxOpenProgressTaskLink(this);
        String startTaskButtonLabel = task.getStatus().getValue().equals("Open") ? "Start Task" : "Stop Task";
        openProgressMenuItem = new AjaxLinkMenuItem("2", startTaskButtonLabel, startStopTaskLink);
        openProgressMenuItem.enabled((!task.isQueued()) && (admin || owner || editable));
        optionsMenu.addItem(openProgressMenuItem);

        closeOpenTaskLink = new AjaxCloseOpenTaskLink(this);
        String closeTaskButtonLabel = task.getStatus().getValue().equals("Closed") ? "Re-Open Task" : "Close Task";
        openClosedMenuItem = new AjaxLinkMenuItem("3", closeTaskButtonLabel, closeOpenTaskLink);
        openClosedMenuItem.enabled((!task.isQueued()) && (admin || owner || editable));
        optionsMenu.addItem(openClosedMenuItem);

        resolveTaskLink = new AjaxResolveTaskLink(this);
        resolutionMenuItem = new AjaxLinkMenuItem("4", "Resolve", resolveTaskLink);
        resolutionMenuItem.enabled((!task.isQueued()) && (admin || owner || editable));
        optionsMenu.addItem(resolutionMenuItem);

        if (task.getStatus().equals(taskService.getClosedStatus())) {
            optionsMenu.hideItem(assignMenuItem);
            optionsMenu.hideItem(resolutionMenuItem);
            optionsMenu.hideItem(openProgressMenuItem);
        }

        return optionsMenu;
    }

    public List<TaskComment> getCommentList() {
        return commentList;
    }

    @Override
    public String getTitle() {
        return "Task: " + task.getName() + " - StaffPM";
    }

    public Task getTask() {
        return task;
    }

    public void updateAjax(AjaxRequestTarget target) {
        task.setUpdatedBy(userService.getCurrentUser());
        task.setLastUpdated(Calendar.getInstance().getTime());
        taskService.updateTask(task);
        taskResDate.setVisible(task.isResolved());
        taskResDateLabel.setVisible(task.isResolved());
        taskResolution.setDefaultModelObject(task.isResolved() ? "Resolved" : "Unresolved");
        taskStatus.setDefaultModelObject(task.getStatus().getValue());
        taskUpdDate.setDefaultModelObject(date.format(task.getLastUpdated()));
        target.addComponent(content);
    }

    public void stopTask(AjaxRequestTarget target) {
        task.setStatus(taskService.getInProgressStatus());
        openProgressMenuItem.updateLabel("Stop Task");
        updateAjax(target);
    }

    public void startTask(AjaxRequestTarget target) {
        task.setStatus(taskService.getOpenStatus());
        openProgressMenuItem.updateLabel("Start Task");
        updateAjax(target);
    }

    public void openTask(AjaxRequestTarget target) {
        task.setStatus(taskService.getOpenStatus());
        task.setResolved(false);
        editPage.setVisible(true);
        openClosedMenuItem.updateLabel("Close Task");
        openProgressMenuItem.updateLabel("Start Task");
        optionsMenu.showItem(assignMenuItem);
        optionsMenu.showItem(resolutionMenuItem);
        optionsMenu.showItem(openProgressMenuItem);
        updateAjax(target);
    }

    public void closeTask(AjaxRequestTarget target) {
        task.setStatus(taskService.getClosedStatus());
        openClosedMenuItem.updateLabel("Re-Open Task");
        openProgressMenuItem.updateLabel("Start Task");
        editPage.setVisible(false);
        optionsMenu.hideItem(assignMenuItem);
        optionsMenu.hideItem(resolutionMenuItem);
        optionsMenu.hideItem(openProgressMenuItem);
        updateAjax(target);
    }

    public void queueTask(AjaxRequestTarget target) {
        task.setQueued(true);
        task.setResolvedBy(userService.getCurrentUser());
        task.setResolvedDate(Calendar.getInstance().getTime());
        taskNotice.setVisible(true);
        editPage.setVisible(false);
        openClosedMenuItem.enabled(false);
        assignMenuItem.enabled(false);
        resolutionMenuItem.enabled(false);
        openProgressMenuItem.enabled(false);
        taskQueueService.addTaskToQueue(task, userService.getCurrentUser(), task.getProject().getOwner(), "");
        updateAjax(target);
        if (!userService.getCurrentUser().equals(task.getAssignee()))
            activityDaoService.notifyAssigneeOfSubmission(task, userService.getCurrentUser());
    }

    public void resolveTask(AjaxRequestTarget target) {
        taskQueueService.addTaskToQueue(task, userService.getCurrentUser(), task.getProject().getOwner(), "");
        List<IModel<TaskQueue>> queuedTasks = new ArrayList<IModel<TaskQueue>>();
        queuedTasks.add(new Model<TaskQueue>(taskQueueService.getQueuedTaskByTaskId(task.getTaskId())));
        feedbackDialog.setQueuedTasks(queuedTasks);
        feedbackDialog.initialise(target);
        feedbackDialog.show(target);
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.pages.management.review.command.IReviewCommandReciever#feedbackCallback(org.apache.wicket.ajax.AjaxRequestTarget)
     */
    @Override
    public void feedbackCallback(AjaxRequestTarget target) {
        task = taskService.getTask(task.getTaskId());
        commentList = taskService.getAllComments(task);
        //If Action Not Cancelled
        if (!task.isQueued()) {
            //Action Accepted
            if (task.isResolved()) {
                openClosedMenuItem.updateLabel("Re-Open Task");
                openProgressMenuItem.updateLabel("Start Task");
                taskResolver.setDefaultModel(new StringModel(task.getResolvedBy().getFullName()));
                taskResolver.setVisible(true);
                taskResolverLabel.setVisible(true);
                optionsMenu.hideItem(assignMenuItem);
                optionsMenu.hideItem(resolutionMenuItem);
                optionsMenu.hideItem(openProgressMenuItem);
            }

            editPage.setVisible(!task.isQueued() && !task.isResolved());
            openClosedMenuItem.enabled(true);
            assignMenuItem.enabled(true);
            resolutionMenuItem.enabled(true);
            openProgressMenuItem.enabled(true);
            taskNotice.setVisible(false);
            reviewMenu.hideAllItems();
        }
        taskResDate.setVisible(task.isResolved());
        taskResDateLabel.setVisible(task.isResolved());
        taskResolution.setDefaultModelObject(task.isResolved() ? "Resolved" : "Unresolved");
        taskStatus.setDefaultModelObject(task.getStatus().getValue());
        taskUpdDate.setDefaultModelObject(date.format(task.getLastUpdated()));
        target.addComponent(content);
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.pages.management.review.command.IReviewCommandReciever#rejectTasks(org.apache.wicket.ajax.AjaxRequestTarget)
     */
    @Override
    public void rejectTasks(AjaxRequestTarget target) {
        List<IModel<TaskQueue>> queuedTasks = new ArrayList<IModel<TaskQueue>>();
        queuedTasks.add(new Model<TaskQueue>(taskQueueService.getQueuedTaskByTaskId(task.getTaskId())));
        rejectDialog.setQueuedTasks(queuedTasks);
        rejectDialog.initialise(target);
        rejectDialog.show(target);
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.pages.management.review.command.IReviewCommandReciever#acceptTasks(org.apache.wicket.ajax.AjaxRequestTarget)
     */
    @Override
    public void acceptTasks(AjaxRequestTarget target) {
        List<IModel<TaskQueue>> queuedTasks = new ArrayList<IModel<TaskQueue>>();
        queuedTasks.add(new Model<TaskQueue>(taskQueueService.getQueuedTaskByTaskId(task.getTaskId())));
        feedbackDialog.setQueuedTasks(queuedTasks);
        feedbackDialog.initialise(target);
        feedbackDialog.show(target);
    }

}
